package Waya::App::FlattenProblems;

use strict;
use warnings;
use Waya::Constants qw/:traverse :colors/;
use Waya::Board;

our $VERSION = '0.1';

use base 'Waya::App::FileIterator';

__PACKAGE__
    ->mk_integer_accessors(qw(counter))
    ->mk_array_accessors(qw(boards sgf));

# sgf list means a list of lines to be emitted together as one sgf file, not a
# list of sgf files.


use constant DEFAULTS => (
    counter => 1,
);

use constant BOARD_SIZE => 19;
use constant SGF_START => sprintf "(;GM[1]FF[4]SZ[%s]", BOARD_SIZE;

# prefix: prefix for filename to use (append possibly counter, and .sgf)
# size: max size of one file, output in several files, numbered from 1

use constant GETOPT => (qw/prefix|p=s size|s=s/);


sub check_options {
    my $self = shift;
    die "--prefix ?\n" unless $self->opt->{prefix};
    if (defined $self->opt->{size}) {
        my $size = $self->opt->{size};
        die "--size, if given, must be an int > 0\n"
            unless $size =~ /^\d+$/ && $size > 0;
    }
}


sub assemble_sgf {
    my $self = shift;
    my $sgf = sprintf "(;GM[1]FF[4]SZ[%s]\n" => BOARD_SIZE;
    $sgf .= join "\n;" => $self->sgf;
    $sgf .= ")\n";
}


sub sgf_length {
    my $self = shift;
    length $self->assemble_sgf;
}


sub emit_sgf {
    my $self = shift;

    my $suffix = '';
    if ($self->opt->{size}) {
        $suffix = sprintf '-%02d' => $self->counter;
    }
    my $filename = sprintf '%s%s.sgf' => $self->opt->{prefix}, $suffix;

    open my $fh, ">$filename" or die "can't open $filename for writing: $!\n";
    printf $fh $self->assemble_sgf;
    close $fh, ">$filename" or die "can't close $filename: $!\n";

    $self->counter_inc;
    $self->sgf_clear;
}


sub app_code {
    my $self = shift;
    $self->SUPER::app_code(@_);

    my $prev_board = Waya::Board->new(board_size => BOARD_SIZE);

    for my $board ($self->boards) {
        # make a diff to the previous board
        
        my %add = (
            BLACK() => [],
            WHITE() => [],
        );
        my @erase;

        for my $x (1..BOARD_SIZE) {
            for my $y (1..BOARD_SIZE) {
                my $coord = Waya::Coordinate->new(x => $x, y => $y);
                my $prev_content = $prev_board->stone_at_coord($coord) || '';
                my $content = $board->stone_at_coord($coord) || '';

                # same as before - go to next coordinate
                next if $prev_content eq $content;

                # current empty but previous not empty - erase the stone;
                if (($content eq '') && ($prev_content ne '')) {
                    push @erase => $coord;
                    next;
                }

                # current not empty but previous empty, or both not empty but
                # different (i.e., white before and now black, or black before
                # and now white) - add the stone
                #
                # $content is BLACK or WHITE

                push @{ $add{$content} } => $coord;
            }
        }

        # now transform the add- and erase-instructions into SGF

        my $sgf_node = '';
        $sgf_node .= 'AE' . join('' => map { "[$_]" } @erase) if @erase;

        $sgf_node .= 'AB' . join('' => map { "[$_]" } @{ $add{BLACK()} })
            if @{ $add{BLACK()} };

        $sgf_node .= 'AW' . join('' => map { "[$_]" } @{ $add{WHITE()} })
            if @{ $add{WHITE()} };

        $self->sgf_push($sgf_node);


        $prev_board = $board;

        $self->emit_sgf if
            defined($self->opt->{size}) &&
            $self->sgf_length >= $self->opt->{size};
    }

    # ok to call it again here; will only emit if something has been added
    # since the last emit.

    $self->emit_sgf;
}


sub process_game {
    my $self = shift;
    $self->clear_should_write;

    $self->game->traverse_node(sub {
        my $node = shift;
        my $has_stones;
        for my $prop (qw/AB AW/) {
            next unless defined $node->{$prop};
            $has_stones++;
        }
        return RC_CONTINUE unless $has_stones;

        my $board = Waya::Board->new(board_size => BOARD_SIZE);

        for my $prop (qw/AB AW/) {
            next unless defined $node->{$prop};
            my $color = ($prop =~ /^AB$/) ? BLACK : WHITE;
            $board->play($_, $color) for ref $node->{$prop} eq 'ARRAY' ?
                @{ $node->{$prop} } : $node->{$prop};
        }

        $self->boards_push($board);
        return RC_PRUNE;
    });
}


1;
